/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openFuyao is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

package program

import (
	"testing"

	"env-check/pkg/config"
	"env-check/pkg/logger"
)

const defaultProgramsLen = 3 // 默认检查程序个数

// createTestConfig 创建测试配置
func createTestConfig() *config.AppConfig {
	return &config.AppConfig{
		ProgramList: []string{"go", "docker", "nonexistent"},
	}
}

// createTestProgramInfo 创建测试程序信息
func createTestProgramInfo() config.ProgramInfo {
	return config.ProgramInfo{
		Name:      "test-program",
		Installed: true,
		Version:   "1.0.0",
		Path:      "/usr/bin/test-program",
	}
}

func TestNewProgramChecker(t *testing.T) {
	cfg := createTestConfig()
	log := logger.NewLogger("")

	checker := NewProgramChecker(cfg, log)

	if checker == nil {
		t.Error("Expected ApplicationChecker instance, got nil")
	}
	if checker.config != cfg {
		t.Error("Config not set correctly")
	}
}

func TestProgramCheckerExecuteSuccess(t *testing.T) {
	cfg := createTestConfig()
	log := logger.NewLogger("")
	checker := NewProgramChecker(cfg, log)

	result, err := checker.Execute()

	assertNoError(t, err)
	assertNotNil(t, result)
	assertEqual(t, len(result.Programs), defaultProgramsLen)
	assertEqual(t, result.Summary.TotalChecked, defaultProgramsLen)
}

func TestProgramCheckerExecuteEmptyProgramList(t *testing.T) {
	cfg := &config.AppConfig{ProgramList: []string{}}
	log := logger.NewLogger("")
	checker := NewProgramChecker(cfg, log)

	result, err := checker.Execute()

	assertNoError(t, err)
	assertNotNil(t, result)
	assertEqual(t, len(result.Programs), 0)
	assertEqual(t, result.Summary.TotalChecked, 0)
}

func TestCheckProgramInstalled(t *testing.T) {
	cfg := createTestConfig()
	log := logger.NewLogger("")
	checker := NewProgramChecker(cfg, log)

	programInfo := checker.checkProgram("go")

	assertEqual(t, programInfo.Name, "go")
	// 我们不关心具体安装状态，只关心函数正常执行
	if programInfo.Installed {
		t.Logf("Go is installed: %s", programInfo.Version)
	} else {
		t.Log("Go is not installed")
	}
}

func TestCheckProgramNotInstalled(t *testing.T) {
	cfg := createTestConfig()
	log := logger.NewLogger("")
	checker := NewProgramChecker(cfg, log)

	programInfo := checker.checkProgram("nonexistent-program-xyz")

	assertEqual(t, programInfo.Name, "nonexistent-program-xyz")
	assertEqual(t, programInfo.Installed, false)
	assertNotEqual(t, programInfo.Error, "")
}

func TestGetProgramVersionSuccess(t *testing.T) {
	cfg := createTestConfig()
	log := logger.NewLogger("")
	checker := NewProgramChecker(cfg, log)

	version, err := checker.getProgramVersion("go")

	if err == nil {
		assertNotEqual(t, version, "")
		t.Logf("Got version: %s", version)
	} else {
		t.Logf("Cannot get version: %v", err)
	}
}

func TestGetProgramVersionCommandError(t *testing.T) {
	cfg := createTestConfig()
	log := logger.NewLogger("")
	checker := NewProgramChecker(cfg, log)

	_, err := checker.getProgramVersion("nonexistent-command")

	assertError(t, err)
}

func TestUpdateProgramSummaryInstalled(t *testing.T) {
	cfg := createTestConfig()
	log := logger.NewLogger("")
	checker := NewProgramChecker(cfg, log)

	result := &config.ProgramCheckResult{
		Summary: config.ProgramCheckSummary{},
	}
	programInfo := createTestProgramInfo()

	checker.updateProgramSummary(programInfo, result)

	assertEqual(t, result.Summary.TotalChecked, 1)
	assertEqual(t, result.Summary.TotalInstalled, 1)
	assertEqual(t, result.Summary.TotalMissing, 0)
}

func TestUpdateProgramSummaryNotInstalled(t *testing.T) {
	cfg := createTestConfig()
	log := logger.NewLogger("")
	checker := NewProgramChecker(cfg, log)

	result := &config.ProgramCheckResult{
		Summary: config.ProgramCheckSummary{},
	}
	programInfo := config.ProgramInfo{
		Name:      "missing-program",
		Installed: false,
		Error:     "not found",
	}

	checker.updateProgramSummary(programInfo, result)

	assertEqual(t, result.Summary.TotalChecked, 1)
	assertEqual(t, result.Summary.TotalInstalled, 0)
	assertEqual(t, result.Summary.TotalMissing, 1)
}

func TestGetSystemInfo(t *testing.T) {
	cfg := createTestConfig()
	log := logger.NewLogger("")
	checker := NewProgramChecker(cfg, log)

	sysInfo := checker.getSystemInfo()

	assertNotNil(t, sysInfo)
	assertNotEqual(t, sysInfo["os"], "")
	assertNotEqual(t, sysInfo["arch"], "")

	t.Logf("OS: %s, Arch: %s", sysInfo["os"], sysInfo["arch"])
}

func assertNotEqual(t *testing.T, actual, expected interface{}) {
	t.Helper()
	if actual == expected {
		t.Fatalf("Expected not equal, but both are %v", actual)
	}
}

// AssertNoError 检查没有错误
func assertNoError(t *testing.T, err error) {
	t.Helper()
	if err != nil {
		t.Fatalf("Expected no error, got: %v", err)
	}
}

// assertNotNil 检查不为nil
func assertNotNil(t *testing.T, actual interface{}) {
	t.Helper()
	if actual == nil {
		t.Fatal("Expected not nil, got nil")
	}
}

func assertEqual(t *testing.T, actual, expected interface{}) {
	t.Helper()
	if actual != expected {
		t.Fatalf("Expected %v, got %v", expected, actual)
	}
}

func assertError(t *testing.T, err error) {
	t.Helper()
	if err == nil {
		t.Fatal("Expected error, got nil")
	}
}
